"""
GCR-Julia Builder Implementation.

Handles building and dependency management for the GCR-Julia package.
Since Julia packages don't require traditional compilation, this focuses on
environment setup, dependency instantiation, and precompilation.
"""

import json
import logging
import subprocess
import time
from pathlib import Path
from typing import Dict, List, Optional, Any

from gcr_solver_manager.extensions.base_builder import (
    BaseBuilder,
    BuildResult
)

logger = logging.getLogger(__name__)


class GCRJuliaBuilder(BaseBuilder):
    """Builder for GCR-Julia package."""
    
    def __init__(self, gcr_julia_path: Path):
        """Initialize the builder."""
        self.gcr_julia_path = gcr_julia_path
        self.project_toml_path = gcr_julia_path / "Project.toml"
        
    def validate_dependencies(self, config: BuildConfig) -> BuildDependencyResult:
        """Validate build dependencies."""
        result = BuildDependencyResult(
            success=True,
            dependencies=[],
            missing_dependencies=[],
            warnings=[],
            system_info={}
        )
        
        try:
            # Check Julia availability
            try:
                julia_result = subprocess.run(
                    ["julia", "--version"],
                    capture_output=True,
                    text=True,
                    timeout=10
                )
                
                if julia_result.returncode == 0:
                    julia_version = julia_result.stdout.strip()
                    result.dependencies.append({
                        "name": "julia",
                        "version": julia_version,
                        "status": "available",
                        "required": True
                    })
                    
                    # Check if Julia version is sufficient (>=1.9)
                    try:
                        version_check = subprocess.run([
                            "julia", "-e", 
                            "if VERSION < v\"1.9\"; exit(1); else; println(\"OK\"); end"
                        ], capture_output=True, timeout=10)
                        
                        if version_check.returncode != 0:
                            result.warnings.append("Julia version <1.9 detected. Julia >=1.9 recommended for package extensions.")
                            
                    except Exception:
                        result.warnings.append("Could not verify Julia version compatibility")
                        
                else:
                    result.missing_dependencies.append({
                        "name": "julia",
                        "reason": "Julia executable not found or failed to run",
                        "required": True
                    })
                    result.success = False
                    
            except subprocess.TimeoutExpired:
                result.missing_dependencies.append({
                    "name": "julia",
                    "reason": "Julia command timed out",
                    "required": True
                })
                result.success = False
            except FileNotFoundError:
                result.missing_dependencies.append({
                    "name": "julia", 
                    "reason": "Julia not found in PATH",
                    "required": True
                })
                result.success = False
                
            # Check GCR-Julia package structure
            if not self.gcr_julia_path.exists():
                result.missing_dependencies.append({
                    "name": "gcr_julia_package",
                    "reason": f"GCR-Julia package not found at {self.gcr_julia_path}",
                    "required": True
                })
                result.success = False
            else:
                if not self.project_toml_path.exists():
                    result.missing_dependencies.append({
                        "name": "project_toml",
                        "reason": "Project.toml not found in GCR-Julia directory",
                        "required": True
                    })
                    result.success = False
                else:
                    result.dependencies.append({
                        "name": "gcr_julia_package",
                        "version": "0.1.0",
                        "status": "available",
                        "path": str(self.gcr_julia_path)
                    })
                    
            # Check optional dependencies
            optional_deps = [
                ("CUDA", "cuda", "GPU acceleration with NVIDIA GPUs"),
                ("AMDGPU", "amdgpu", "GPU acceleration with AMD GPUs"), 
                ("MPI", "mpi", "Distributed computing"),
                ("PETSc_jll", "petsc", "PETSc integration")
            ]
            
            for pkg_name, dep_id, description in optional_deps:
                try:
                    check_result = subprocess.run([
                        "julia", "-e", 
                        f"try; using {pkg_name}; println(\"available\"); catch; println(\"unavailable\"); end"
                    ], capture_output=True, text=True, timeout=15)
                    
                    if check_result.returncode == 0:
                        status = check_result.stdout.strip()
                        result.dependencies.append({
                            "name": dep_id,
                            "package": pkg_name,
                            "status": status,
                            "required": False,
                            "description": description
                        })
                        
                        if status == "unavailable":
                            result.warnings.append(f"Optional dependency {pkg_name} not available: {description}")
                    else:
                        result.warnings.append(f"Could not check optional dependency {pkg_name}")
                        
                except Exception as e:
                    result.warnings.append(f"Error checking {pkg_name}: {e}")
                    
            # System info
            try:
                system_result = subprocess.run([
                    "julia", "-e", 
                    "using InteractiveUtils; versioninfo()"
                ], capture_output=True, text=True, timeout=10)
                
                if system_result.returncode == 0:
                    result.system_info["julia_versioninfo"] = system_result.stdout
                    
            except Exception as e:
                logger.debug(f"Could not get system info: {e}")
                
        except Exception as e:
            logger.error(f"Dependency validation failed: {e}")
            result.success = False
            result.missing_dependencies.append({
                "name": "validation",
                "reason": f"Dependency validation error: {e}",
                "required": True
            })
            
        return result
    
    def build(self, config: BuildConfig) -> BuildResult:
        """Build the GCR-Julia package."""
        build_start_time = time.time()
        
        result = BuildResult(
            success=False,
            build_time=0.0,
            artifacts=[],
            logs=[],
            warnings=[],
            metadata={}
        )
        
        try:
            result.logs.append("Starting GCR-Julia package build...")
            
            # Validate dependencies first
            dep_result = self.validate_dependencies(config)
            if not dep_result.success:
                result.logs.append("Dependency validation failed")
                result.logs.extend([f"Missing: {dep['name']}" for dep in dep_result.missing_dependencies])
                return result
                
            # Change to GCR-Julia directory
            if not self.gcr_julia_path.exists():
                result.logs.append(f"GCR-Julia path does not exist: {self.gcr_julia_path}")
                return result
                
            result.logs.append(f"Working in directory: {self.gcr_julia_path}")
            
            # Step 1: Instantiate project dependencies
            result.logs.append("Instantiating Julia project dependencies...")
            instantiate_cmd = [
                "julia", 
                f"--project={self.gcr_julia_path}",
                "-e", 
                "using Pkg; Pkg.instantiate()"
            ]
            
            instantiate_result = subprocess.run(
                instantiate_cmd,
                cwd=self.gcr_julia_path,
                capture_output=True,
                text=True,
                timeout=300  # 5 minutes for instantiate
            )
            
            if instantiate_result.returncode != 0:
                result.logs.append("Package instantiation failed:")
                result.logs.append(instantiate_result.stderr)
                return result
            else:
                result.logs.append("Package instantiation successful")
                result.artifacts.append({
                    "name": "Manifest.toml",
                    "path": str(self.gcr_julia_path / "Manifest.toml"),
                    "type": "dependency_lock"
                })
                
            # Step 2: Precompile package
            result.logs.append("Precompiling GCR-Julia package...")
            precompile_cmd = [
                "julia",
                f"--project={self.gcr_julia_path}",
                "-e",
                "using Pkg; Pkg.precompile()"
            ]
            
            precompile_result = subprocess.run(
                precompile_cmd,
                cwd=self.gcr_julia_path,
                capture_output=True,
                text=True,
                timeout=600  # 10 minutes for precompilation
            )
            
            if precompile_result.returncode != 0:
                result.warnings.append("Package precompilation failed, but continuing...")
                result.logs.append(precompile_result.stderr)
            else:
                result.logs.append("Package precompilation successful")
                
            # Step 3: Test basic package loading
            result.logs.append("Testing basic package loading...")
            load_test_cmd = [
                "julia",
                f"--project={self.gcr_julia_path}",
                "-e",
                "using GCR; println(\"GCR.jl loaded successfully - $(length(names(GCR))) exports available\")"
            ]
            
            load_result = subprocess.run(
                load_test_cmd,
                cwd=self.gcr_julia_path,
                capture_output=True,
                text=True,
                timeout=60
            )
            
            if load_result.returncode != 0:
                result.logs.append("Package loading test failed:")
                result.logs.append(load_result.stderr)
                return result
            else:
                result.logs.append(f"Package loading test passed: {load_result.stdout.strip()}")
                
            # Step 4: Run basic functionality test
            result.logs.append("Running basic functionality test...")
            func_test_cmd = [
                "julia",
                f"--project={self.gcr_julia_path}",
                "-e", """
                using GCR, LinearAlgebra;
                n = 10;
                A = generate_test_matrix(Float64, n; type=:laplacian);
                b = rand(n);
                result = solve_gcr(A, b, tol=1e-6);
                println("Basic GCR test: converged in $(result.iterations) iterations");
                """
            ]
            
            func_result = subprocess.run(
                func_test_cmd,
                cwd=self.gcr_julia_path,
                capture_output=True,
                text=True,
                timeout=30
            )
            
            if func_result.returncode != 0:
                result.warnings.append("Basic functionality test failed")
                result.logs.append(func_result.stderr)
            else:
                result.logs.append(f"Basic functionality test passed: {func_result.stdout.strip()}")
                
            # Build successful
            result.success = True
            result.logs.append("GCR-Julia package build completed successfully")
            
            # Add metadata
            result.metadata = {
                "package_path": str(self.gcr_julia_path),
                "julia_project": True,
                "precompiled": precompile_result.returncode == 0,
                "functional_test": func_result.returncode == 0,
                "build_type": "julia_package"
            }
            
        except subprocess.TimeoutExpired as e:
            result.logs.append(f"Build timed out: {e}")
        except Exception as e:
            result.logs.append(f"Build error: {e}")
            logger.error(f"Build failed: {e}")
            
        finally:
            result.build_time = time.time() - build_start_time
            
        return result
    
    def clean(self, config: BuildConfig) -> bool:
        """Clean build artifacts."""
        try:
            # Remove Julia compilation cache
            clean_cmd = [
                "julia",
                f"--project={self.gcr_julia_path}",
                "-e",
                "using Pkg; Pkg.gc()"
            ]
            
            result = subprocess.run(
                clean_cmd,
                capture_output=True,
                timeout=30
            )
            
            logger.info(f"GCR-Julia clean completed (exit code: {result.returncode})")
            return result.returncode == 0
            
        except Exception as e:
            logger.error(f"Clean failed: {e}")
            return False
    
    def get_build_info(self) -> Dict[str, Any]:
        """Get build information."""
        return {
            "builder": "GCRJuliaBuilder",
            "package_path": str(self.gcr_julia_path),
            "build_system": "Julia Pkg manager",
            "requires_compilation": False,
            "supports_parallel_build": True,
            "estimated_build_time": "1-5 minutes",
            "disk_space_required": "~50MB"
        }